#pragma once

#include <iostream>
#include <strings.h>
#include <string>
#include <unistd.h>
#include <memory>

#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#include "InetAddr.hpp"
#include "Log.hpp"

static const int defaultsockfd = -1;
static const int defaultbacklog = 8;

//封装 socket
namespace SocketArea
{
    using namespace LogArea;

    class Socket;

    using sock_ptr = std::shared_ptr<Socket>;

    class Socket
    {
    public:
        virtual ~Socket() = default;
        virtual void SocketOrDie() = 0;
        virtual bool BindOrDie(int port) = 0;
        virtual bool ListenOrDie() = 0;
        virtual sock_ptr Accepter(InetAddr* client) = 0;
        virtual void Close() = 0;
        virtual int Recv(std::string* out_message) = 0;
        virtual int Send(const std::string& in_message) = 0;
        virtual int SockFd() = 0;

        // 提供一个创建 sockfd 的固定套路
        void BulidListenSockfd(int port)
        {
            SocketOrDie();
            BindOrDie(port);
            ListenOrDie();
        }
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket():_sockfd(defaultsockfd)
        {
        }
        TcpSocket(int sockfd):_sockfd(sockfd)
        {
        }

        virtual void SocketOrDie() override
        {
            _sockfd = ::socket(AF_INET,SOCK_STREAM,0);
            if(_sockfd<0)
            {
                LOG(LogLevel::FATALER)<<"socket error";
                DIE(SOCKET_ERR);
            }
            LOG(LogLevel::DEBUG)<<"socket success";
        }

        virtual bool BindOrDie(int port) override
        {
            if(_sockfd<0) return false;
            InetAddr addr(port);
            int n = ::bind(_sockfd,addr.NetAddr(),addr.AddrLen());
            if(n<0)
            {
                LOG(LogLevel::FATALER)<<"bind error";
                DIE(BIND_ERR);
            }

            LOG(LogLevel::DEBUG)<<"bind success";
            return true;
        }

        virtual bool ListenOrDie() override
        {
            if(_sockfd<0) return false;

            int n = ::listen(_sockfd,defaultbacklog);
            if(n<0)
            {
                LOG(LogLevel::FATALER)<<"listen error";
                DIE(LISTEN_ERR);
            }
            LOG(LogLevel::DEBUG)<<"listen success";

            return true;
        }

        virtual sock_ptr Accepter(InetAddr* client) override
        {
            if(!client) return nullptr;
            struct sockaddr_in peer;
            bzero(&peer,sizeof(peer));
            socklen_t len = sizeof(peer);
            int newsockfd = ::accept(_sockfd,CONV(&peer),&len);
            if(newsockfd<0)
            {
                LOG(LogLevel::WARNING)<<"accept warning";
                return nullptr;
            }

            client->SetAddr(peer);
            return std::make_shared<TcpSocket>(newsockfd);
        }

        virtual void Close() override
        {
            if(_sockfd<0) return;
            ::close(_sockfd);
        }

        virtual int Recv(std::string* out_message) override
        {
            if(_sockfd<0) return 0;
            char buffer[1024];
            int size = ::recv(_sockfd,buffer,sizeof(buffer)-1,0);
            if(size<0)
                LOG(LogLevel::WARNING)<<"recvfrom warning";
            buffer[size] = 0;
            *out_message = buffer;
            LOG(LogLevel::INFO)<<"recv a message: "<<buffer;
            return size;
        }

        virtual int Send(const std::string& in_message)override
        {
            if(_sockfd<0) return 0;
            int size = ::send(_sockfd,in_message.c_str(),in_message.size(),0);
            if(size<0)
                LOG(LogLevel::WARNING)<<"send warning";
            return size;
        }

        virtual int SockFd() override
        {
            return _sockfd;
        }
        ~TcpSocket()
        {
        }

    private:
        int _sockfd;
    };
}